package praktikumid.w11.p2;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

/**
 * Strings - simple operations with objects.
 * A list of objects, where new objects can
 * be added, existing objects can be modified
 * and existing objects can be deleted.
 * <p>
 * We use this simple program to demonstrate
 * undo implementation possibilities.
 * <p>
 * This program is very simple and does not
 * handle any exceptions. This is so to 
 * keep the code simple. Of course, in real
 * world problems (and also in class exercises),
 * all exceptions should be handled properly!
 * 
 * @author Ago
 *
 */
public class ElementUndo {
	/**
	 * Simple element, which technically only has
	 * one String - name. 
	 * @author Ago
	 *
	 */
	static class Element {
		/**
		 * Name of the element.
		 */
		String name;
		
		boolean visible = true;
		
		/**
		 * Constructor, where we set the name.
		 * @param name
		 */
		public Element(String name) {
			this.name = name;
		}
		
		@Override
		public String toString() {
			// let's have a "nice" printout
			return name;
		}
		
		@Override
		protected Object clone() throws CloneNotSupportedException {
			Element e = new Element(this.name);
			e.visible = this.visible;
			return e;
		}
	}
	
	static class Action {
		static final int ACTION_ADD = 1;
		static final int ACTION_MODIFY = 2;
		static final int ACTION_REMOVE = 3;
		
		int type = ACTION_ADD;
		
		Element originalElement; // ref to elements
		Element newElement; // in case of modifying
		public Action(Element e) {
			// in case of add and remove
			originalElement = e;
			if (!e.visible) {
				// in case of remove
				type = ACTION_REMOVE;
			}
		}
		
		public Action(Element e1, Element e2) {
			originalElement = e1;
			newElement = e2;
			type = ACTION_MODIFY;
		}
		
		@Override
		public String toString() {
			return type + " " + originalElement + " " + newElement;
		}
		
		/*
		public Action(Element e, int type) {
			originalElement = e;
			this.type = type;
		}
		*/
	}
	
	/**
	 * A list of elements.
	 */
	static List<Element> elements = new ArrayList<Element>();
	
	static List<Action> actions = new ArrayList<Action>();
	static int actionIndex = -1;
	
	/**
	 * Main.
	 * @param args arguments.
	 * @throws IOException Dealing with input can throw exception.
	 * @throws CloneNotSupportedException 
	 */
	public static void main(String[] args) throws IOException, CloneNotSupportedException {

		BufferedReader br = new BufferedReader(
				new InputStreamReader(System.in));
		while(true) {
			printHelp();
			String line = br.readLine();
			// we expect correct input
			// just split everything by space
			String[] tokens = line.split(" ");
			int op = Integer.valueOf(tokens[0]);
			Element e;
			int elid = -1;
			if (op > 0 && op < 4) {
				actionIndex++;
				while (actions.size() > actionIndex) {
					actions.remove(actions.size() - 1);
				}
			}
			switch (op) {
			case 1: // add element
				e = new Element(tokens[1]);
				elements.add(e);
				actions.add(actionIndex, new Action(e));
				break;
			case 2: // modify element, rename
				elid = Integer.valueOf(tokens[1]);
				e = elements.get(elid);
				Element eOriginal = (Element) e.clone();
				// same as:
				// Element eOriginal = new Element();
				// eOriginal.name = e.name;
				// eOriginal.visible = e.visible
				e.name = tokens[2];
				actions.add(new Action(eOriginal, e));
				break;
			case 3: // delete element
				elid = Integer.valueOf(tokens[1]);
				//elements.remove(elid);
				e = elements.get(elid);
				e.visible = false;
				actions.add(new Action(e));
				break;
			case 4: // printout
				printElements();
				break;
			case 5:
				undo();
				break;
			case 0:
				System.exit(0);
				
			}
			
			debugHistory();
			
		}
	}
	
	public static void undo() {
		if (actions.size() != 0) {
			Action a = actions.get(actionIndex);
			switch (a.type) {
			case Action.ACTION_ADD:
				a.originalElement.visible = false;
				break;
			case Action.ACTION_MODIFY:
				/*
				int idx = elements.indexOf(a.newElement);
				if (idx > -1) {
					elements.set(idx, a.originalElement);
				}
				*/
				a.newElement.name = a.originalElement.name;
				break;
			case Action.ACTION_REMOVE:
				a.originalElement.visible = true;
				break;
			}
		}
		actionIndex--;
	}
	
	public static void debugHistory() {
		System.out.println("actionIndex:" + actionIndex);
		for (int i = 0; i < actions.size(); i++) {
			System.out.println(i + ". " + actions.get(i));
		}
	}
	/**
	 * Prints out the elements in the list.
	 */
	public static void printElements() {
		for (int i = 0; i < elements.size(); i++) {
			if (elements.get(i).visible) {
				System.out.println(i + ". " + elements.get(i));
			}
		}
	}
	
	/**
	 * Prints out help - what can be done with the program.
	 */
	public static void printHelp() {
		System.out.println("  1 add element [1 name]");
		System.out.println("  2 change element [2 elid name]");
		System.out.println("  3 delete element [3 elid]");
		System.out.println("  4 list elements [4]");
		System.out.println("  5 undo [5]");
		System.out.println("  0 exit [0]");
	}
}

